19章 制御構造の問題
akht.icon
制御構造の一般的な問題
19.1 論理式
論理評価はtrue, falseを使う
0, 1を使わない
truthy, falsyよりもブール値を使う
ブール値とtrue, falseは暗黙に照合する
if (hoge == true) みたいに書かない
複雑な式は単純化する
中間的なブール変数を作って分割する
ブール関数として独立させる
新しい関数の名前は、評価の目的をコードで文書化する点で、プログラムを抽象化する役割を果たす。コメントを使って評価を文書化するよりは、こちらの方がよい。コメントよりもコードが読まれる可能性の方が高いし、最新の状態に保たれる可能性も高い。
複雑な条件はテーブルにまとめて参照する(テーブル駆動方式)
肯定的な論理式にする
肯定的でない短くない文字列を理解するのに一切苦労しないわけではない人々は少なくない。
ド・モルガンの定理を使う
かっこを使って論理式を明確化する
論理式の優先順位は覚えるのが難しい
論理式が評価される方法を知る
式全体を評価するものもあれば、短絡評価、遅延評価もある
短絡評価のある言語ではこういうのに注意
if ( ( denominator != 0 ) && ( ( item / denominator ) > MIN_VALUE ) ) ...
if ( ( ( item / denominator ) > MIN_VALUE ) && ( denominator != 0 ) ) ...
論理式としては等価だが、後者はゼロ除算エラーになる可能性がある
数値を含む式は数直線の順番に並べる
これが意識されてると助かるakht.icon
0との比較のガイドライン
プログラミング言語において、Oにはいろんな意味があるので比較するときは明確にしようという話
論理式に共通する問題
Cの派生言語では定数を比較の左側に置く
定数には代入できないという仕様を利用して、比較==ではなく代入=してしまったことに気付けるようにする
C++では、&&、||、==用のプリプロセッサマクロの作成を(あくまでも最後の手段として)検討する
へぇ〜akht.icon
Javaでの等値(==)、等価(equals)
19.2 複合文(ブロック)
中括弧は対で書く
今はエディタが入力支援してくれる
19.3 null文
C++においては、セミコロンだけで構成された文のこと
(割愛)
19.4 危険なほど深いネストの回避
インデントが多すぎる、つまり「ネストしている」問題は、コンピュータ関連の文献で25年も前から非難されているにもかかわらず、いまだに難解なコードの主犯格である。
NoamChomskyとGeraldWeinbergの研究では、3レベル以上のif文のネストを理解できる人はほとんどいないことが示されている(Yourdon1986a)。
↓ネストを減らす方法をいくつか紹介
条件文を再評価して、ネストしたif文を単純化する
その代償として、単純な論理評価が少し複雑になってしまうかもしれない
break文を使って、ネストしたif文を単純化する
特殊な方法なのでチームの了承が必要
ネストしたif文をif-then-else文に置き換える
テストがないと手を入れづらい...akht.icon
ネストしたif文をcase文に置き換える
これはいいakht.icon
深くネストしたコードをルーチンとして独立させる
コードは本書参照
よりオブジェクト指向的なアプローチでのぞむ
ポリモーフィズムをうまく使えばかなりよくなる
深くネストしたコードの設計を見直す
一部の専門家は、case文はほぼ必ずと言ってよいほどコードのファクタリングがうまくいっていないことを示すもので、それが必要になることはまれであると論じている(Meyer1997)。
ポリモーフィックなメソッド呼び出しでファクトリメソッドに書き換えたりできる
ルーチンを修正しなければならないと決まっているわけではないが、そうしないのなら、それを説明する十分な理由がなければならない。
hai...akht.icon
19.5 プログラミングの基礎:構造化プログラミング
ダイクストラが提唱者
構造化プログラミングの核心は、プログラムは入口が1つ、出口が1つの制御構造を使用すべきであるという単純な考え方である。
構造化プログラミングとは、構造化されたトップダウン形式の設計のことではない。構造化プログラミングはコードの詳細レベルにしか適用されない。
構造化プログラミングの3つの要素
連続(sequence)
順番に実行されるステートメントの集合
選択(selection)
ステートメントが選択的に実行されるような制御構造(if-then-else, caseなど)
反復(iteration)
ステートメントのグループが複数回実行されるような制御構造(for, whileなど)
構造化プログラミングが登場するまでは、goto文を使用することが制御フローの究極のツールだった。
私に言わせるなら、構造化プログラミングの3つの構造以外の制御構造(break、continue、return、throwcatchなど)が使われていたら、批判的な目で見るべきである。
まじですか...akht.icon
19.6 制御構造と複雑さ
制御構造に多くのページを割いてきた理由の1つは、制御構造がプログラムの全体的な複雑さを大きく左右するからである。
複雑さはどれくらい重要か
「有能なプログラマは自分の脳みそがほんのちょっとしかないことを十分承知している。だから、とても謙虚な姿勢でプログラミングにのぞむ」(Dijkstra1972)
制御フローの複雑さが重要なのは、信頼性の低さやエラーの多さと相関関係にあるためだ(McCabe1976;Shenetal.1985)
複雑を軽減するための一般的なガイドライン
複雑さを定量化する方法
ルーチンの判定ポイントを数える
https://gyazo.com/748f6a0af6760506c56c0cc5a9ac8a55
複雑さを定量化したらどうするか
判定ポイントの個数に応じて対処する
ルーチンの一部を別のルーチンに移動しても全体の複雑さは軽減されない
ただし、一度に対処しないといけない複雑さは減る
頭の中のお手玉の数を減らすことには意味がある
その他の複雑さ(上記の判定ポイント以外)
使用するデータの量
制御構造のネストのレベル
コードの行数
変数への参照と参照の間に挟まれている行数(持続間隔)
変数が使用されている行数(寿命)
入出力の量
19.7 まとめ
論理式を単純で読みやすいものにすることは、コードの品質に大きく貢献する。
深いネストはルーチンの理解を妨げる。さいわい、この問題は比較的簡単に防ぐことができる。
構造化プログラミングは、今でもその価値を失っていないシンプルな概念である。どのようなプログラムも、連続、選択、反復の組み合わせで作成することができる。
複雑さを最小限に抑えることは、高品質なコードを書くための鍵である。